import ol from '@/static/openlayers/ol.js';
import { EPSG3857, EPSG4326, __checkLayerVector, __findLayer, initDrawElements } from ".";
import projzh from "projzh";
import { city_polygon, wfs_point, flyTo_style, pointAnimation_style } from "./style";
import { transPoints, fromLonLat } from "./transfrom";

export const baiduXYZSource = () => {
  const url = "http://192.168.1.21:8091/map/";
  const resolutions = [];
  for (let z = 0; z <= 18; z++) { resolutions[z] = Math.pow(2, 18 - z); };
  const bmercResolutions = new ol.proj.Projection({
    code: EPSG4326,
    extent: ol.extent.applyTransform([-180.0019, -71.8742, 180.0019, 73.8970], projzh.ll2bmerc),
    units: 'm',
    axisOrientation: 'neu',
    global: false
  })
  ol.proj.addProjection(bmercResolutions);
  return new ol.source.XYZ({
    projection: EPSG3857,
    tileGrid: new ol.tilegrid.TileGrid({
      origin: [0, 0],
      resolutions,
      tileSize: [256, 256]
    }),
    tilePixelRatio: 2,
    tileUrlFunction: (tileCoord) => {
      if (!tileCoord) return;
      let z = tileCoord[0];
      let x = tileCoord[1];
      let y = -tileCoord[2] - 1;
      if (x < 0) x = 'M' + (-x);
      if (y < 0) y = 'M' + (-y)
      return `${url}${z}/${x}/${y}.png`;
    }
  });
}

export const OSM = new ol.layer.Tile({ source: new ol.source.OSM(), zIndex: 0, name: "osm", opacity: 1 });

export const baiduXYZ = new ol.layer.Tile({ source: baiduXYZSource(), name: "baseLayer", opacity: 0.99, zIndex: 1 });

export async function featureEffect(map, data) {
  const { layer, source } = __checkLayerVector.call(this, map, "featureLayer", "featureSource");
  const cluster = clusterSource(source);
  layer.setZIndex(12);
  layer.setStyle(wfs_point);
  const wmsServerSource = new ol.format.GeoJSON().readFeatures(data, { featureProjection: 'EPSG:3857' })
  source.addFeatures(wmsServerSource);
  map.on("moveend", e => {
    const zoom = map.getView().getZoom();
    if (zoom >= 7) layer.setSource(source);
    else layer.setSource(cluster);
  });
}

export const clusterSource = (source) => {
  return new ol.source.Cluster({ distance: 40, source });
}

export const heatLayer = (source) => {
  return new ol.layer.Heatmap({ source, blur: 15, radius: 15, name: "heat-map" });
}

export function addBoundary(map, polyline, name, style = city_polygon) {
  const { layer, source } = __checkLayerVector.call(this, map, "CityBoundaryLayer", "CityBoundarySource");
  layer.setZIndex(11);
  const _polygon = new ol.Feature({ geometry: new ol.geom.Polygon([transPoints(polyline)]) });
  _polygon.setStyle(style);
  _polygon.setId(name);
  source.addFeature(_polygon);
  layer.setZIndex(10);
}

export const flyTo = (map, center, done) => {
  var duration = 1500;
  var zoom = map.getView().getZoom();
  var parts = 2;
  var called = false;
  function callback(complete) {
    --parts;
    if (called) return
    if (parts === 0 || !complete) {
      called = true;
      done(complete);
    }
  }
  map.getView().animate({ center, duration }, callback);
  map.getView().animate({ zoom: zoom - 1, duration: duration / 2 }, { zoom: zoom + 1, duration: duration / 2 }, callback);
}

export class FlyLine {
  constructor (map, x, y) {
    const { layer, source } = __checkLayerVector.call(this, map, "flyLineLayer", "flyLineSource");
    this.flights = Array.apply(null, { length: 10 }).map(() => this.addRandomFeature());
    const baseLayer = __findLayer(map, "name", "baseLayer");
    layer.setStyle(this.styleFunc);
    layer.setZIndex(11);
    this.pointsPerMs = 0.01;
    this.baseLayer = baseLayer;
    this.layer = layer;
    this.source = source;
    this.x = x;
    this.y = y;
    this.map = map;
    this.init();
    this.duration = 10000;
  }
  init() {
    this.flights.forEach((item, index) => {
      var arcGenerator = new arc.GreatCircle({ x: this.x, y: this.y }, { x: item[0], y: item[1] });
      var arcLine = arcGenerator.Arc(100, { offset: 10 });
      const features = [];
      arcLine.geometries.forEach(geometry => {
        const line = new ol.geom.LineString(geometry.coords);
        line.transform('EPSG:4326', 'EPSG:3857');
        features.push(new ol.Feature({ geometry: line, finished: false, }));
      });
      this.addLater.call(this, features, index * 10, this.source);
      this.animateFlights();
    });
  }
  flash(pos) {
    var start = new Date().valueOf();
    const point = initDrawElements.FeatureFactory("Point", fromLonLat(pos));
    this.source.addFeature(point);
    var listenerKey = this.baseLayer.on('postrender', animate.bind(this));
    function animate(event) {
      var vectorContext = ol.render.getVectorContext(event);
      var frameState = event.frameState;
      var flashGeom = point.getGeometry().clone();
      var elapsed = frameState.time - start;
      var elapsedRatio = elapsed / this.duration;
      var radius = ol.easing.easeOut(elapsedRatio) * 25 + 5;
      var opacity = ol.easing.easeOut(1 - elapsedRatio);
      vectorContext.setStyle(pointAnimation_style(radius, opacity));
      vectorContext.drawGeometry(flashGeom);
      if (elapsed > this.duration) {
        return ol.Observable.unByKey(listenerKey);
      }
      this.map.render();
    }
  }
  styleFunc(feature) {
    if (feature.get('finished')) return flyTo_style
    else return null;
  }
  addRandomFeature() {
    var x_max = 120, x_min = 110;
    var y_max = 26, y_min = 34;
    var x = Math.random() * (x_max - x_min + 1) + x_min;
    var y = Math.random() * (y_max - y_min + 1) + y_min;
    return [x, y]
  }
  animateFlights() {
    var listener = this.baseLayer.on('postrender', animate.bind(this));
    function animate (event) {
      var vectorContext = ol.render.getVectorContext(event);
      var frameState = event.frameState;
      vectorContext.setStyle(flyTo_style);
      var features = this.source.getFeatures();
      for (let i = 0; i < features.length; i++) {
        const feature = features[i];
        if (!feature.get('finished')) {
          // 只画动画尚未完成的线
          const coords = feature.getGeometry().getCoordinates();
          const elapsedTime = frameState.time - feature.get('start');
          if (elapsedTime >= 0) {
            const elapsedPoints = elapsedTime * this.pointsPerMs;
            if (elapsedPoints >= coords.length) feature.set('finished', true);
            const maxIndex = Math.min(elapsedPoints, coords.length);
            const currentLine = new ol.geom.LineString(coords.slice(0, maxIndex));
            // 动画需要在当前和最近的被包装的世界
            const worldWidth = ol.extent.getWidth(this.map.getView().getProjection().getExtent());
            const offset = Math.floor(this.map.getView().getCenter()[0] / worldWidth);
            // 直接用向量上下文绘制直线
            currentLine.translate(offset * worldWidth, 0);
            vectorContext.drawGeometry(currentLine);
            currentLine.translate(worldWidth, 0);
            vectorContext.drawGeometry(currentLine);
            if (maxIndex >= 100) {
              ol.Observable.unByKey(listener);
              // requestAnimationFrame 渲染帧过快，这里我们使用节流
              clearTimeout(this.timer);
              this.timer = setTimeout(() => {
                this.flights.forEach(item => {
                  this.flash(item);
                });
              }, 300);
            }
          }
        }
      }
      this.map.render();
    }
  }
  addLater(features, timeout, source) {
    setTimeout(() => {
      let start = Date.now();
      features.forEach((feature) => {
        feature.set('start', start);
        source.addFeature(feature);
        const duration = (feature.getGeometry().getCoordinates().length - 1) / this.pointsPerMs;
        start += duration;
      });
    }, timeout);
  }
}

